import socket
import select
import time
from datetime import datetime
import cv2

def compress_camera_frame(frame, format='.jpg', quality=80):
    """
    压缩摄像头捕获的图像帧
    
    参数:
        frame: 从摄像头读取的帧(numpy数组)
        format: 压缩格式(.jpg或.png)
        quality: 压缩质量参数，JPG为0-100，PNG为0-9
        
    返回:
        压缩后的字节数据和压缩信息
    """
    if frame is None:
        raise ValueError("输入帧为空，无法压缩")
    
    # 根据格式设置压缩参数
    if format.lower() == '.jpg':
        # 确保质量在有效范围内
        quality = max(0, min(100, quality))
        encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), quality]
    elif format.lower() == '.png':
        # PNG的压缩级别，0-9
        quality = max(0, min(9, quality))
        encode_param = [int(cv2.IMWRITE_PNG_COMPRESSION), quality]
    else:
        raise ValueError("仅支持.jpg和.png格式")
    
    # 执行压缩
    success, encoded = cv2.imencode(format, frame, encode_param)
    if not success:
        raise RuntimeError("图像压缩失败")
    
    # 转换为字节数据
    compressed_data = encoded.tobytes()
    
    # 计算压缩信息
    original_size = frame.nbytes
    compressed_size = len(compressed_data)
    compression_ratio = compressed_size / original_size * 100
    
    return {
        'data': compressed_data,
        'original_size': original_size,
        'compressed_size': compressed_size,
        'ratio': compression_ratio,
        'format': format
    }
# 配置
HOST = '0.0.0.0'
PORT = 8004
HEARTBEAT_TIMEOUT = 5 # 心跳超时时间（秒）
#MESSAGE_INTERVAL = 0  # 发送消息间隔（秒）
HEARTBEAT_MSG = b'HEARTBEAT'

# 初始化
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.setblocking(False)
server_socket.bind((HOST, PORT))

cap = cv2.VideoCapture(0)

# 维护客户端列表： {address: last_heartbeat_time}
clients = {}

print(f"服务端启动，监听 {HOST}:{PORT}")

last_send_time = time.time()

while True:
    # 使用 select 监听可读事件（仅监听服务端套接字）
    readable, _, _ = select.select([server_socket], [], [], 1)
    try:
        for sock in readable:
            if sock == server_socket:
                # 接收数据
                data, client_addr = server_socket.recvfrom(1024)


                # 处理心跳
                if data == HEARTBEAT_MSG:
                    clients[client_addr] = datetime.now()
                    #print(f"收到客户端 {client_addr} 心跳，更新时间")
                else:
                    # 非心跳数据
                    print(f"收到客户端 {client_addr} 其他数据: {data.decode()}")
    except ConnectionResetError as e:
        '''对方关闭了连接，而我们正准备接收'''
        ...    
    # 获取当前时间
    last_send_time = time.time()

    # 清理过期客户端
    expired_cilents = [addr for addr, time in clients.items()
                        if (datetime.now() - time).total_seconds() > HEARTBEAT_TIMEOUT]
    for addr in expired_cilents:
        del clients[addr]
        print(f"客户端 {addr} 心跳超时，已移除")
    
    # 读取一帧图像
    ret, frame = cap.read()
    if not ret:
        continue
    
    # 压缩
    result = compress_camera_frame(frame, '.jpg', 50)
    byte_array = result['data']
    
    # 将图像编码为JPEG格式，得到字节流
    #_, encoded_image = cv2.imencode('.jpg', frame)

    # 将编码后的图像数据转换为byte数组
    #byte_array = encoded_image.tobytes()
    #print(len(byte_array))

    # 向有效客户端发送消息
    for client_addr in clients:
        try:
            #message = f"服务器消息 {datetime.now().strftime('%H:%M:%S')}".encode()
            server_socket.sendto(byte_array, client_addr)
            #print(f"向 {client_addr} 发送消息")
        except OSError as e:
            ...

cap.release()
cv2.destroyAllWindows()
